package server

import (
	"os"

	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/config"
	"github.com/go-kratos/kratos/v2/config/file"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware"
	"github.com/go-kratos/kratos/v2/registry"
	"github.com/go-kratos/kratos/v2/transport/grpc"
	"github.com/go-kratos/kratos/v2/transport/http"

	"gitee.com/shuokeyun/kratos/conf"
)

// App .
type App struct {
	config              *conf.Bootstrap
	logger              log.Logger
	configFilePath      string
	appConfBootstrap    interface{}
	withAfterGRPCServer WithAfterGRPCServerFunc
	withAfterHTTPServer WithAfterHTTPServerFunc
	withInitApp         func(*App, log.Logger) ([]WithOption, func(), error)
	grpcMiddleware      []middleware.Middleware
	httpMiddleware      []middleware.Middleware
	discovery           registry.Discovery
	registrar           registry.Registrar
}

// Option 设置配置信息
func (y *App) Option(options ...WithOption) {
	for _, v := range options {
		v.apply(y)
	}
}

// Run 启动
func (y *App) Run() {
	registrar, discovery, err := NewRegistrar(y.config.GetRegistry())
	if err != nil {
		panic(err)
	}
	y.discovery = discovery
	y.registrar = registrar
	op, cleanup, err := y.withInitApp(y, y.GetLogger())
	if err != nil {
		panic(err)
	}
	y.Option(op...)

	defer cleanup()

	app, err := y.initApp()
	if err != nil {
		panic(err)
	}

	// start and wait for stop signal
	if err := app.Run(); err != nil {
		panic(err)
	}
}

// GetConfigBootstrap 获取公共的配置信息
func (y *App) GetConfigBootstrap() *conf.Bootstrap {
	return y.config
}

// GetAppConfigBootstrap 获取app的配置信息
func (y *App) GetAppConfigBootstrap() interface{} {
	return y.appConfBootstrap
}

// GetLogger 获取日志接口
func (y *App) GetLogger() log.Logger {
	return y.logger
}

// GetDiscovery 获取服务发现接口
func (y *App) GetDiscovery() registry.Discovery {
	return y.discovery
}

func (y *App) LoadConfig() {
	c := config.New(
		config.WithSource(
			file.NewSource(y.configFilePath),
		),
	)
	var err error
	if err = c.Load(); err != nil {
		panic(err)
	}

	var bc conf.Bootstrap
	if err = c.Scan(&bc); err != nil {
		panic(err)
	}

	if bc.GetServer() == nil {
		panic("server配置不能为空")
	}
	if bc.GetServer().GetId() == "" {
		bc.GetServer().Id, err = os.Hostname()
		if err != nil {
			panic(err)
		}
	}
	if y.appConfBootstrap != nil {
		if err = c.Scan(y.appConfBootstrap); err != nil {
			panic(err)
		}
	}
	y.config = &bc
}

func (y *App) initApp() (*kratos.App, error) {
	httpServer := NewHTTPServer(y.config.GetServer(), y.logger, y.httpMiddleware)
	err := y.afterHTTPServer(httpServer)
	if err != nil {
		return nil, err
	}
	grpcServer := NewGRPCServer(y.config, y.logger, y.grpcMiddleware)
	err = y.afterGRPCServer(grpcServer)
	if err != nil {
		return nil, err
	}
	app := y.newKratosApp(httpServer, grpcServer)
	return app, nil
}

func (y *App) afterGRPCServer(gs *grpc.Server) error {
	if y.withAfterGRPCServer != nil {
		return y.withAfterGRPCServer(gs)
	}
	return nil
}

func (y *App) afterHTTPServer(hs *http.Server) error {
	if y.withAfterHTTPServer != nil {
		return y.withAfterHTTPServer(hs)
	}
	return nil
}

func (y *App) newKratosApp(hs *http.Server, gs *grpc.Server) *kratos.App {
	server := y.config.GetServer()
	op := []kratos.Option{
		kratos.ID(server.GetId()),
		kratos.Name(server.GetName()),
		kratos.Version(server.GetVersion()),
		kratos.Metadata(map[string]string{}),
		kratos.Logger(y.logger),
		kratos.Server(
			hs,
			gs,
		),
	}
	if y.registrar != nil {
		op = append(op, kratos.Registrar(y.registrar))
	}
	return kratos.New(op...)
}
